package channelService

import (
	".go-fox-edge-channel-s7-client/channelEntity"
	".go-fox-edge-channel-s7-client/channelEvent"
	".go-fox-edge-channel-s7-client/utils"
	"encoding/hex"
	"errors"
	"gitee.com/fierce_wolf/go-fox-edge-channel-common/channelLogger"
	"gitee.com/fierce_wolf/go-fox-edge-common/commRedisRpc"
	"gitee.com/fierce_wolf/go-fox-edge-common/commUtil/Hex"
	"gitee.com/fierce_wolf/go-fox-edge-common/commUtil/Json"
	"gitee.com/fierce_wolf/go-fox-edge-common/commUtil/Map"
	"gitee.com/fierce_wolf/go-fox-edge-common/commUtil/Method"
	"gitee.com/fierce_wolf/go-fox-edge-common/commUtil/Number"
	"gitee.com/fierce_wolf/go-fox-edge-common/commUtil/Reflect"
	"gitee.com/fierce_wolf/go-fox-edge-common/commUtil/String"
	"gitee.com/fierce_wolf/gos7"
	"math"
	"strings"
)

type executeService struct {
}

func (e *executeService) execute(clientEntity *channelEntity.S7ClientEntity, requestVO *commRedisRpc.ChannelRequestVO) (*commRedisRpc.ChannelRespondVO, error) {
	if requestVO.Send == nil || requestVO.Send == "" {
		return nil, errors.New("发送数据不能为空！")
	}
	if requestVO.Timeout == 0 {
		return nil, errors.New("超时参数不能为空！")
	}

	// 获得连接
	val, ok := s7connector.channelName2Client.Load(requestVO.Name)
	if !ok {
		return nil, errors.New("与远端尚未建立连接:" + requestVO.Name)
	}
	client := val.(gos7.Client)

	// 检查：参数是否合法
	if requestVO.Send == nil || !Reflect.IsMap(requestVO.Send) {
		return nil, errors.New("命令参数格式必须为JSOM格式:" + requestVO.Name)
	}

	send := requestVO.Send.(map[string]interface{})

	// 打印日志
	channelLogger.PrintChannelDebugLogger(requestVO.Name, "发送数据", requestVO.Send)

	// 执行操作
	recv, err := e.doMethod(client, send)
	if err != nil {
		if s7connector.isOpError(err) {
			channelEvent.Invalid.Notify(Json.BuildJson(clientEntity))
		}
		return nil, err
	}

	respondVO := commRedisRpc.Type.ChannelRespondVO.Ok(requestVO)
	respondVO.Recv = recv
	return respondVO, nil
}

func (e *executeService) doMethod(client gos7.Client, request map[string]interface{}) (map[string]interface{}, error) {
	method := Map.GetString(request, "method", "")
	params := Map.GetSlice(request, "params", make([]interface{}, 0))

	if Method.HasEmpty(method, params) {
		return nil, errors.New("参数格式缺失: method, params")
	}

	if method == "readData" {
		list, err := e.readData(client, params)
		if err != nil {
			return nil, err
		}
		result := make(map[string]interface{})
		result["method"] = "readData"
		result["list"] = list

		return result, nil
	}
	if method == "writeData" {
		return nil, e.writeData(client, params)
	}

	return nil, nil
}

func (e *executeService) readData(client gos7.Client, params []interface{}) ([]map[string]interface{}, error) {
	var helper gos7.Helper

	list := make([]map[string]interface{}, 0)

	for _, v := range params {
		param := v.(map[string]interface{})

		address := Map.GetString(param, "address", "")
		dataType := Map.GetString(param, "dataType", "")
		count := Map.GetInt(param, "count", 1)

		if Map.HasNull(param, "count") || address == "" || dataType == "" {
			return nil, errors.New("参数格式缺失: count, address, dataType")
		}
		if count < 1 {
			return nil, errors.New("count小于1")
		}

		address = strings.ToUpper(address)
		dataType = strings.ToUpper(dataType)

		s7addr, err := utils.S7Addr.ParseS7Addr(address)
		if err != nil {
			return nil, err
		}
		if s7addr.Area == "" {
			return nil, errors.New("地址格式不正确：" + address)
		}

		area, err := s7addr.GetS7AddrCode()
		if err != nil {
			return nil, err
		}

		// 场景1：写入bit
		if s7addr.BitAddr >= 0 && dataType == "BOOL" {
			buffer := make([]byte, 1)
			err = client.ReadBytes(area, s7addr.DBNumber, s7addr.ByteAddr, len(buffer), buffer)
			if err != nil {
				return nil, err
			}

			param = Map.Clone(param)
			param["value"] = helper.GetBoolAt(buffer[0], uint(s7addr.BitAddr))
			list = append(list, param)
			continue
		}

		if dataType == "INT16" {
			buffer := make([]byte, 2)
			err = client.ReadBytes(area, s7addr.DBNumber, s7addr.ByteAddr, len(buffer), buffer)
			if err != nil {
				return nil, err
			}

			value := int16(0)
			helper.GetValueAt(buffer, 0, &value)
			param = Map.Clone(param)
			param["value"] = value
			list = append(list, param)
			continue
		}
		if dataType == "UINT16" {
			buffer := make([]byte, 2)
			err = client.ReadBytes(area, s7addr.DBNumber, s7addr.ByteAddr, len(buffer), buffer)
			if err != nil {
				return nil, err
			}

			value := uint16(0)
			helper.GetValueAt(buffer, 0, &value)
			param = Map.Clone(param)
			param["value"] = value
			list = append(list, param)
			continue
		}
		if dataType == "UINT32" || dataType == "TIME" {
			buffer := make([]byte, 4)
			err = client.ReadBytes(area, s7addr.DBNumber, s7addr.ByteAddr, len(buffer), buffer)
			if err != nil {
				return nil, err
			}

			value := uint32(0)
			helper.GetValueAt(buffer, 0, &value)
			param = Map.Clone(param)
			param["value"] = value
			list = append(list, param)
			continue
		}
		if dataType == "FLOAT32" {
			buffer := make([]byte, 4)
			err = client.ReadBytes(area, s7addr.DBNumber, s7addr.ByteAddr, len(buffer), buffer)
			if err != nil {
				return nil, err
			}

			value := float32(0)
			helper.GetValueAt(buffer, 0, &value)
			param = Map.Clone(param)
			param["value"] = value
			list = append(list, param)
			continue
		}
		if dataType == "FLOAT64" {
			buffer := make([]byte, 8)
			err = client.ReadBytes(area, s7addr.DBNumber, s7addr.ByteAddr, len(buffer), buffer)
			if err != nil {
				return nil, err
			}

			value := float64(0)
			helper.GetValueAt(buffer, 0, &value)
			param = Map.Clone(param)
			param["value"] = value
			list = append(list, param)
			continue
		}
		if dataType == "BYTE" {
			buffer := make([]byte, count)
			err = client.ReadBytes(area, s7addr.DBNumber, s7addr.ByteAddr, len(buffer), buffer)
			if err != nil {
				return nil, err
			}

			param = Map.Clone(param)
			param["value"] = hex.EncodeToString(buffer)
			list = append(list, param)
			continue
		}
		if dataType == "STRING" {
			buffer := make([]byte, count+2)
			err = client.ReadBytes(area, s7addr.DBNumber, s7addr.ByteAddr, len(buffer), buffer)
			if err != nil {
				return nil, err
			}

			param = Map.Clone(param)
			param["value"] = helper.GetStringAt(buffer, 0)
			list = append(list, param)
			continue
		}

	}

	return list, nil
}

func (e *executeService) writeData(client gos7.Client, params []interface{}) error {
	var helper gos7.Helper

	for _, v := range params {
		param := v.(map[string]interface{})

		address := Map.GetString(param, "address", "")
		dataType := Map.GetString(param, "dataType", "")
		value := Map.GetOrDefault(param, "value", nil)

		if value == nil || address == "" || dataType == "" {
			return errors.New("参数格式缺失: value, address, dataType")
		}

		address = strings.ToUpper(address)
		dataType = strings.ToUpper(dataType)

		s7addr, err := utils.S7Addr.ParseS7Addr(address)
		if err != nil {
			return err
		}
		if s7addr.Area == "" {
			return errors.New("地址格式不正确：" + address)
		}

		area, err := s7addr.GetS7AddrCode()
		if err != nil {
			return err
		}

		// 场景1：写入bit
		if s7addr.BitAddr >= 0 && dataType == "BOOL" {
			err = client.WriteBit(area, s7addr.DBNumber, s7addr.ByteAddr, s7addr.BitAddr, value.(bool))
			if err != nil {
				return err
			}
			continue
		}

		// 场景2：写入BYTE
		buffer := make([]byte, 0)
		if dataType == "INT16" {
			value = int16(Number.MakeInt64(value))
			buffer = make([]byte, 2)
			helper.SetValueAt(buffer, 0, value)
		}
		if dataType == "UINT16" {
			value = uint16(Number.MakeInt64(value))
			buffer = make([]byte, 2)
			helper.SetValueAt(buffer, 0, value)
		}
		if dataType == "UINT32" || dataType == "TIME" {
			value = uint32(Number.MakeInt64(value))
			buffer = make([]byte, 4)
			helper.SetValueAt(buffer, 0, value)
		}
		if dataType == "FLOAT32" {
			buffer = make([]byte, 4)
			helper.SetValueAt(buffer, 0, math.Float32bits(float32(Number.MakeFloat64(value))))
		}
		if dataType == "FLOAT64" {
			value = float64(Number.MakeFloat64(value))
			buffer = make([]byte, 8)
			helper.SetValueAt(buffer, 0, math.Float64bits(Number.MakeFloat64(value)))
		}
		if dataType == "BYTE" {
			buffer = Hex.DecodeString(String.ToString(value))
		}
		if dataType == "STRING" {
			// 字符串编码需要加上2字节的前缀
			buffLen := 2 + len(value.(string))
			buffer = make([]byte, buffLen)
			helper.SetStringAt(buffer, 0, buffLen, value.(string))
		}
		if len(buffer) == 0 {
			continue
		}

		// 写入数组
		err = client.WriteBytes(area, s7addr.DBNumber, s7addr.ByteAddr, len(buffer), buffer)
		if err != nil {
			return err
		}

	}

	return nil
}

func (e *executeService) read(client gos7.Client, area string, dbNumber, start, count int) ([]byte, error) {
	if start < 0 || count < 0 || dbNumber < 0 || count > 2048 {
		return nil, errors.New("dbNumber, start, count：大小超出有效范围")
	}

	if strings.ToUpper(area) == "DB" {
		buff := make([]byte, count)
		err := client.AGReadDB(dbNumber, start, count, buff)
		return buff, err
	}
	if strings.ToUpper(area) == "CT" {
		buff := make([]byte, count)
		err := client.AGReadCT(start, count, buff)
		return buff, err
	}
	if strings.ToUpper(area) == "EB" {
		buff := make([]byte, count)
		err := client.AGReadEB(start, count, buff)
		return buff, err
	}
	if strings.ToUpper(area) == "MB" {
		buff := make([]byte, count)
		err := client.AGReadMB(start, count, buff)
		return buff, err
	}
	if strings.ToUpper(area) == "TM" {
		buff := make([]byte, count)
		err := client.AGReadTM(start, count, buff)
		return buff, err
	}

	return nil, errors.New("不支持的area：" + area)
}

func (e *executeService) write(client gos7.Client, area string, dbNumber, start, count int, buff []byte) error {
	if start < 0 || count < 0 || dbNumber < 0 || count > 2048 {
		return errors.New("dbNumber, start, count：大小超出有效范围")
	}

	if count != len(buff) {
		return errors.New("buff大小和count不一致")
	}
	if strings.ToUpper(area) == "DB" {
		return client.AGWriteDB(dbNumber, start, count, buff)
	}
	if strings.ToUpper(area) == "CT" {
		return client.AGWriteCT(start, count, buff)
	}
	if strings.ToUpper(area) == "EB" {
		return client.AGWriteEB(start, count, buff)
	}
	if strings.ToUpper(area) == "MB" {
		return client.AGWriteMB(start, count, buff)
	}
	if strings.ToUpper(area) == "TM" {
		return client.AGWriteTM(start, count, buff)
	}
	if strings.ToUpper(area) == "AB" {
		return client.AGWriteAB(start, count, buff)
	}

	return errors.New("不支持的area：" + area)
}
